<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="FIBjB" id="FIBjB"><span data-lake-id="u59e6e34c" id="u59e6e34c">典型回答</span></h1>
  <p data-lake-id="ucd68d467" id="ucd68d467"><br></p>
  <p data-lake-id="ud3d7f74a" id="ud3d7f74a"><strong><span data-lake-id="u1fcf761b" id="u1fcf761b">RocketMQ的消息想要确保不丢失，需要生产者、消费者以及Broker的共同努力，缺一不可。</span></strong></p>
  <p data-lake-id="ufd0f561f" id="ufd0f561f"><span data-lake-id="u9e262b9d" id="u9e262b9d">​</span><br></p>
  <p data-lake-id="u37f9096c" id="u37f9096c"><span data-lake-id="u254488fd" id="u254488fd">首先</span><strong><span data-lake-id="u09a14529" id="u09a14529">在生产者端，消息的发送分为同步和异步两种</span></strong><span data-lake-id="u5a662c0a" id="u5a662c0a">，在同步发送消息的情况下，消息的发送会同步阻塞等待Broker返回结果，在Broker确认收到消息之后，生产者才会拿到SendResult。如果这个过程中发生异常，那么就说明消息发送可能失败了，就需要生产者进行重新发送消息。</span></p>
  <p data-lake-id="u4ccd9e7d" id="u4ccd9e7d"><span data-lake-id="uba378e44" id="uba378e44">​</span><br></p>
  <p data-lake-id="u677bb2ea" id="u677bb2ea"><span data-lake-id="u80a8e52e" id="u80a8e52e">但是Broker其实并不会立即把消息存储到磁盘上，而是先存储到内存中，内存存储成功之后，就返回给确认结果给生产者了。然后再通过异步刷盘的方式将内存中的数据存储到磁盘上。但是这个过程中，如果机器挂了，那么就可能会导致数据丢失。</span></p>
  <p data-lake-id="ufb1e5ab4" id="ufb1e5ab4"><span data-lake-id="u4a7321d3" id="u4a7321d3">​</span><br></p>
  <p data-lake-id="u9a406a82" id="u9a406a82"><span data-lake-id="uf8562606" id="uf8562606">如果</span><strong><span data-lake-id="uec40a568" id="uec40a568">想要保证消息不丢失，可以将消息保存机制修改为同步刷盘</span></strong><span data-lake-id="ub0999e10" id="ub0999e10">，这样，Broker会在同步请求中把数据保存在磁盘上，确保保存成功后再返回确认结果给生产者。</span></p>
  <p data-lake-id="u603bf30f" id="u603bf30f"><span data-lake-id="u63e326b2" id="u63e326b2">​</span><br></p>
  <pre lang="java"><code>
## 默认情况为 ASYNC_FLUSH 
flushDiskType = SYNC_FLUSH 
</code></pre>
  <p data-lake-id="u1fcf9213" id="u1fcf9213"><br></p>
  <p data-lake-id="u6fce0854" id="u6fce0854"><span data-lake-id="udbbab992" id="udbbab992">除了同步发送消息，还有异步发送，</span><strong><span data-lake-id="ua7fbf0fb" id="ua7fbf0fb">异步发送的话就需要生产者重写SendCallback的onSuccess和onException方法，用于给Broker进行回调。在方法中实现消息的确认或者重新发送。</span></strong></p>
  <p data-lake-id="ue193d923" id="ue193d923"><span data-lake-id="u7fcfd598" id="u7fcfd598">​</span><br></p>
  <p data-lake-id="uf641ef7d" id="uf641ef7d"><strong><span data-lake-id="u52237c9a" id="u52237c9a">为了保证消息不丢失，RocketMQ肯定要通过集群方式进行部署，Broker 通常采用一主多从部署方式，并且采用主从同步的方式做数据复制。</span></strong></p>
  <p data-lake-id="u4a6456e2" id="u4a6456e2"><span data-lake-id="u960b6cd8" id="u960b6cd8">​</span><br></p>
  <p data-lake-id="u8639ed0e" id="u8639ed0e"><span data-lake-id="ua3272dc9" id="ua3272dc9">当主Broker宕机时，从Broker会接管主Broker的工作，保证消息不丢失。同时，RocketMQ的Broker还可以配置多个实例，消息会在多个Broker之间进行冗余备份，从而保证数据的可靠性。</span></p>
  <p data-lake-id="u9354e7b0" id="u9354e7b0"><span data-lake-id="ua6a863f0" id="ua6a863f0">​</span><br></p>
  <p data-lake-id="u07bdbec9" id="u07bdbec9"><span data-lake-id="u8332ff2f" id="u8332ff2f">默认方式下，Broker在接收消息后，写入 master 成功，就可以返回确认响应给生产者了，接着消息将会异步复制到 slave 节点。但是如果这个过程中，Master的磁盘损坏了。那就会导致数据丢失了。</span></p>
  <p data-lake-id="ufb5a3dea" id="ufb5a3dea"><span data-lake-id="ucbbe2ea4" id="ucbbe2ea4">​</span><br></p>
  <p data-lake-id="u8d36e39f" id="u8d36e39f"><span data-lake-id="u2aeb3322" id="u2aeb3322">如果</span><strong><span data-lake-id="u94460f87" id="u94460f87">想要解决这个问题，可以配置同步复制的方式，即Master在将数据同步到Slave节点后，再返回给生产者确认结果。</span></strong><span data-lake-id="u02edd83d" id="u02edd83d" class="lake-fontsize-11" style="color: rgb(85, 85, 85)"><br><br></span></p>
  <pre lang="java"><code>
## 默认为 ASYNC_MASTER
brokerRole=SYNC_MASTER
</code></pre>
  <p data-lake-id="u707db954" id="u707db954"><br></p>
  <p data-lake-id="ucf514a1a" id="ucf514a1a"><span data-lake-id="uf6c28353" id="uf6c28353">在消费者端，需要确保在消息拉取并消费成功之后再给Broker返回ACK，就可以保证消息不丢失了，如果这个过程中Broker一直没收到ACK，那么就可以重试。</span></p>
  <p data-lake-id="ud0362f30" id="ud0362f30"><span data-lake-id="u40ba0ba7" id="u40ba0ba7">​</span><br></p>
  <p data-lake-id="ue9d3ee7d" id="ue9d3ee7d"><span data-lake-id="ub7a22c44" id="ub7a22c44">所以，</span><strong><span data-lake-id="u37237f66" id="u37237f66">在消费者的代码中，一定要在业务逻辑的最后一步</span></strong><code data-lake-id="u25f41586" id="u25f41586"><strong><span data-lake-id="u78fa2b84" id="u78fa2b84">return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;</span></strong></code><strong><span data-lake-id="u66973beb" id="u66973beb"> </span></strong><span data-lake-id="uafd1d00e" id="uafd1d00e">当然，也可以先把数据保存在数据库中，就返回，然后自己再慢慢处理。</span></p>
  <p data-lake-id="u967f1b84" id="u967f1b84"><span data-lake-id="u7bab2b19" id="u7bab2b19">​</span><br></p>
  <p data-lake-id="u29e1aa0e" id="u29e1aa0e"><span data-lake-id="u9a49991f" id="u9a49991f">但是，需要注意的是RocketMQ和Kafka一样，只能最大限度的保证消息不丢失，但是没办法做到100%保证不丢失。</span></p>
 </body>
</html>